#include <sys/uio.h> // iovec
#include <unistd.h>  // write()
// #include <errno.h>

#include "./Buffer.h"

Buffer::Buffer(size_t initReadWriteSize)
    : buffer_(kInitPrependSize + initReadWriteSize),
      readableIndex_(kInitPrependSize), // 注意：索引从预留区后开始
      writableIndex_(kInitPrependSize)
{
}

ssize_t Buffer::readFd(int fd, int *saveErrno)
{
    // Buffer不一定能容纳一次读取数据
    // 用readv()分散读：1.Buffer空闲可写区 2.Buffer空闲可写区不够存到额外栈extraBuf
    // 3.对Buffer扩容，转移extraBuf到Buffer空闲可写区
    // 避免多次系统调用，不影响数据接收

    // struct iovec
    // {
    //     ptr_t iov_base; // 指向的缓冲区存放readv()读的数据或writev()要写的数据
    //     size_t iov_len; // 读的最大长度或实际写的长度
    // };
    struct iovec iovecBuf[2]{};

    const size_t writableSize = this->getWritableSize();
    // iovecBuf[0].iov_base = this->begin() + this->writableIndex_;
    iovecBuf[0].iov_base = this->writableBegin();
    iovecBuf[0].iov_len = writableSize;

    char extraBuf[65536]{0}; // 65536/1024 = 64KB
    iovecBuf[1].iov_base = extraBuf;
    iovecBuf[1].iov_len = sizeof(extraBuf);

    // TODO: 优化约定是否使用额外缓冲区标准
    // 这里是：Buffer空闲可写区 >= extraBuf 64KB
    // 那么：启用extraBuf时，Buffer空闲可写区最大64KB-1，extraBuf 64KB，则最多容纳64KB-1+64KB的数据
    const int iovecBufCnt = (writableSize >= sizeof(extraBuf)) ? 1 : 2; // 够用就1块否则2块空间

    const ssize_t n = ::readv(fd, iovecBuf, iovecBufCnt);
    if (n < 0)
    {
        *saveErrno = errno;
    }
    else if (n <= writableSize) // Buffer空闲可写区足够
    {
        // Buffer空闲可写区已写
        this->writableIndex_ += n; // 更新索引
    }
    else // Buffer空闲可写区不够，只能写writableSize写满，extraBuf要写n-writableSize
    {
        // Buffer空闲可写区已写
        this->writableIndex_ = this->buffer_.size();
        this->appendWrite(extraBuf, n - writableSize); // 扩容并追加
    }
    return n;
}

void Buffer::appendWrite(const char *data, size_t needWriteSize)
{
    // ensureWritableBytes(len);
    if (this->getWritableSize() < needWriteSize) // 空闲可写区不足
    {
        this->expandCapacity(needWriteSize); // 扩容
    }

    std::copy(data, data + needWriteSize, this->writableBegin()); // 源起始，源结束，目的起始
    this->writableIndex_ += needWriteSize;
}

void Buffer::expandCapacity(size_t needWriteSize)
{
    // 预留区+空闲读区+空闲可写区 < 预留区 + 实际想写大小
    // 预留区始终不动！
    // 空闲可读区+空闲可写区 < 实际想写大小
    // 不足， 后边开辟空间
    if (this->getPrependableSize() + this->getWritableSize() < kInitPrependSize + needWriteSize)
    {
        this->buffer_.resize(this->writableIndex_ + needWriteSize);
    }
    else // 足够，前边开辟空间
    {
        // 预留区始终不动！
        // 有数据可读区 移动到 空闲读区
        // std::copy(this->begin() + this->readableIndex_, // 源起始
        //           this->begin() + this->writableIndex_, // 源结束
        //           this->begin() + kInitPrependSize);    // 目的起始
        // OPTIMIZE: 复用已有方法，间接调用获取
        std::copy(this->readableBegin(),             // 源起始
                  this->writableBegin(),             // 源结束
                  this->begin() + kInitPrependSize); // 目的起始

        this->readableIndex_ = this->kInitPrependSize;
        this->writableIndex_ = this->readableIndex_ + this->getReadableSize();
    }
}

size_t Buffer::getPrependableSize() const
{
    return this->readableIndex_;
}

size_t Buffer::getReadableSize() const
{
    return this->writableIndex_ - this->readableIndex_;
}

size_t Buffer::getWritableSize() const
{
    return this->buffer_.size() - this->writableIndex_;
}

char *Buffer::begin()
{
    // vector底层数组首元素的地址
    return &*this->buffer_.begin();
}

const char *Buffer::begin() const
{
    return &*this->buffer_.begin();
}

char *Buffer::readableBegin()
{
    return this->begin() + this->readableIndex_;
}

const char *Buffer::readableBegin() const
{
    return this->begin() + this->readableIndex_;
}

char *Buffer::writableBegin()
{
    return this->begin() + this->writableIndex_;
}

const char *Buffer::writableBegin() const
{
    return this->begin() + this->writableIndex_;
}

ssize_t Buffer::writeFd(int fd, int *saveErrno)
{
    // 有数据可读区 读 最大getReadableSize()
    ssize_t n = ::write(fd, this->readableBegin(), this->getReadableSize());
    if (n < 0)
    {
        *saveErrno = errno;
    }
    return n;
}

std::string Buffer::getAllReadableData()
{
    return this->getReadableData(this->getReadableSize()); // 复用，间接调用获取
}

std::string Buffer::getReadableData(size_t needReadSize)
{
    std::string result(this->readableBegin(), needReadSize);
    this->readableIndexReset(needReadSize);
    return result;
}

void Buffer::readableIndexReset(size_t needReadSize)
{
    // 上层只读了 有数据可读区 的部分
    if (needReadSize < this->getReadableSize()) // 读部分
    {
        this->readableIndex_ += needReadSize;
    }
    else // 读所有
    {
        this->indexReset();
    }
}

void Buffer::indexReset()
{
    // 注意：索引从预留区后开始
    this->readableIndex_ = kInitPrependSize;
    this->writableIndex_ = kInitPrependSize;
}